home *** CD-ROM | disk | FTP | other *** search
- /* Micronas vpx 3224D/3225D video processor i2c device driver.
- Copyright Cherry George Mathew <cherry@freeshell.org>
-
- Based on Frodo Looijaard's <frodol@dds.nl> document on
- writing i2c clients. See Documentation/i2c/writing-clients.
-
- Special thanks to the i2c authors for excellent code templates and
- useful documentation.
-
- */
-
- /* This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
- /* #include <linux/version.h> */
- #include <linux/module.h>
- #include <linux/pci.h>
- #include <linux/i2c.h>
- #include <linux/init.h>
- #include <linux/spinlock.h>
- #include <linux/byteorder/generic.h>
- #include <linux/byteorder/swab.h>
- #include <linux/sched.h>
- #include <linux/videodev.h>
- #include <asm/io.h>
- #include <asm/bitops.h>
- #include "pvcard.h"
- #include "pvproc.h"
-
- /* Module params. */
- static int debug;
-
- /* Functional level debugging */
- #define dprintk(fmt, args...) if (debug>=1) printk(KERN_DEBUG "pvcl-debug: " fmt, ## args);
- /* Debugging single functions */
- #define tprintk(fmt, args...) if (debug>=2) printk(KERN_DEBUG "pvcl-debug: " fmt, ## args);
- /* Warning - too verbose. Debugging port conversations. */
- #define vprintk(fmt, args...) if (debug>=3) printk(KERN_DEBUG "pvcl-debug:" fmt, ## args);
-
-
- /* module parameters:
- */
-
- const char *client_name = "vpx i2c bus interface";
- static int re_entry = 0;
-
- /* currently, vmode is the only "sticky" status variable in vpx.
- * Oh, yes, and vpx_client.
- */
- static __u16 vmode;
-
- /* This client pointer contains the address of a detected vpx chip, if any.
- * Multiple chips, are unlikely on the same adapter, and are ignored.
- * In such cases, the chip with a lower I2C address is detected.
- */
-
- static struct i2c_client * vpx_client;
-
-
- int vpx_init_client(struct i2c_client *client);
- int vpx_attach_adapter(struct i2c_adapter *adapter);
- int vpx_detach_client(struct i2c_client *client);
- int vpx_command(struct i2c_client *client, unsigned int cmd, void *arg);
- void vpx_inc_use(struct i2c_client *client);
- void vpx_dec_use(struct i2c_client *client);
- void vpx_poll_fp_busy(struct i2c_client *client);
-
- /* TODO: Clearup I2C_DRIVERID_EXP0 issue on the v4l list. */
-
- struct i2c_driver vpx_driver =
- {
- name: "Micronas vpx 3224/5d i2c driver",
- id: I2C_DRIVERID_EXP0,
- flags: I2C_DF_NOTIFY,
- attach_adapter: &vpx_attach_adapter,
- detach_client: &vpx_detach_client,
- command: &vpx_command,
- inc_use: &vpx_inc_use,
- dec_use: &vpx_dec_use
- };
-
-
-
-
- /* Scan ports 0x43 - 0x47 (7-bit addresses) for the vpx */
-
- static unsigned short normal_i2c[] = { I2C_CLIENT_END };
-
- static unsigned short normal_i2c_range[] = { 0x43, 0x47, I2C_CLIENT_END };
-
- /* Magic definition of all other variables and things */
- I2C_CLIENT_INSMOD;
-
-
- /* byte banging routines. i2c-algo-bit.c (lines 363 - 364 assumes data
- to be little endian. Am I right or have I read the code wrong ?
- */
-
-
- s32 vpx_read_byte(struct i2c_client *client, u8 reg)
- {
- /* byte-sized register */
- return i2c_smbus_read_byte_data(client,reg);
- }
-
- s32 vpx_read_word(struct i2c_client *client, u8 reg)
- {
- /* word-sized register */
-
- #ifdef __LITTLE_ENDIAN
- /* The vpx takes MSB first. OK. topsy turvy! */
-
- return swab16(i2c_smbus_read_word_data(client,reg));
-
- #endif
-
- return i2c_smbus_read_word_data(client,reg);
- }
-
- s32 vpx_write_byte(struct i2c_client *client, u8 reg, u16 value)
- {
-
- /* byte-sized register */
- return i2c_smbus_write_byte_data(client,reg,value);
-
- }
-
- s32 vpx_write_word(struct i2c_client *client, u8 reg, u16 value)
- {
- /* word-sized register */
- /* Do the byte swap */
-
- #ifdef __LITTLE_ENDIAN
-
- value=swab16(value);
-
- #endif
- return i2c_smbus_write_word_data(client,reg,value);
- }
-
-
- s32 vpx_fp_write(struct i2c_client *client, u16 fp_reg, u16 value)
- {
- int status;
- vpx_poll_fp_busy(client);
- if((status=vpx_write_word(client, FPWR, fp_reg))) return status;
- vpx_poll_fp_busy(client);
- return vpx_write_word(client, FPDAT, (value & 0x3fff));
- }
-
- s32 vpx_fp_read(struct i2c_client *client, u16 fp_reg)
- {
- s32 status;
- vpx_poll_fp_busy(client);
- if((status=vpx_write_word(client, FPRD, fp_reg))) return status;
- vpx_poll_fp_busy(client);
- return (0x3fff & vpx_read_word(client, FPDAT));
- }
-
- void vpx_poll_fp_busy(struct i2c_client *client)
- {
-
- unsigned long status;
- status = (unsigned long) vpx_read_byte(client, FPSTA);
- while(test_bit(2, &status))
- {
- status = (unsigned long) vpx_read_byte(client, FPSTA);
-
- /* This is a half hearted attempt to sleep
- * for 20 milliseconds. The vpx manual says that if
- * the onboard FP (FP stands for
- * "Fast Processor" :-O ) is busy, read/write
- * to it needs to be retried after 20ms.
- * The priority here is for other processes
- * to run. Not for us to encourage
- * couch potatoes... ;)
- */
-
- set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(VPX_BUSY_TIMEOUT);
- }
-
- }
-
-
- /* vpx functional layer */
-
- /* initialize the video processor */
-
- void vpx_pinit(struct i2c_client *client, int model)
- {
- int portword;
- /* I've gone to the trouble of customizing vpx parameters here
- * with extensibility in mind. For each card, there should be a
- * special set of settings for the vpx.
- */
-
- switch(model)
- {
- case PVCLPP_COMBO:
- /* The pvclpp combo card uses a philips TEA5582
- * FM demodulator to demodulate sound.
- * The TEA5582 has a mute control, which is connected
- * via VPort B, bit 1. Therefore, we have to guess that
- * the video capture takes place via the 8bits of
- * VPort A. This is set via FPreg, 0x154.
- */
-
- portword = vpx_fp_read(client, 0x154) | 0x302;
- tprintk("Writing %04x to FPreg 0x154 \n.",
- portword);
-
- vpx_fp_write(client, 0x154, portword);
-
-
- /* Set VPort Driver strength.
- * Got this from the default setting of the pvclpp
- * application (tvtap) for win98. Booted into
- * windows, ran tvtap, killed it with softice,
- * warm booted into linux through int 19,
- * and read the I2C
- * registers.
- */
-
- vpx_write_byte(client, 0xf9, 0x24);
- vpx_write_byte(client, 0xf8, 0x24);
-
- /* VREF pulse width = 6.
- * Guess how I found out ?? ;-)
- */
-
- vpx_fp_write(client, 0x153, 0x20);
-
- /* Set the input source. There are 3 input
- * sources, VIN1, VIN2, VIN3.
- * FPReg(0x21) [1:0] determines
- * hardcoding to television tuner input
- * for just now. */
-
- portword = vpx_fp_read(client, 0x21) & 0xfc;
-
- vpx_fp_write(client, 0x21, portword | 0x01);
-
- }
-
- /* Disable Winload table #2. We use Winload table #1 for both fields.*/
-
- vpx_fp_write(client, 0x12B, 0xc00);
-
- /* Latch current Values */
- portword = vpx_fp_read(client, 0x140);
- portword |= 0x40;
- vpx_fp_write(client, 0x140, portword);
-
-
- /* Switch off slicer. */
- vpx_write_byte(client, 0xaa, 0x40);
-
- tprintk("I2C reg. 0xAA reads %02x. \n",
- vpx_read_byte(client, 0xaa));
-
-
-
- /* Set to PAL - B, G, H, I (50Hz) for testing on my VCR.
- * Here is the vpx manual listing for setting various standards.
- *
- * FPReg(0x20) [2:0] Standard Vert. IF. (MHz)
- * 0 PAL B,G,H,I 50Hz 4.433618
- * 1 NTSC M 60Hz 3.579545
- * 2 SECAM 50Hz 4.286
- * 3 NTSC44 60Hz 4.433618
- * 4 PAL M 60Hz 3.575611
- * 5 PAL N 50Hz 3.582056
- * 6 PAL 60 60Hz 4.433618
- * 7 NTSC COMB 60Hz 3.579545
- */
-
-
- portword = vpx_fp_read(client, 0x20) & 0xff8;
-
- vpx_fp_write(client, 0x20, portword | 0x00);
-
- /* There's some confusion in the ITU 601 format. Cr and Cb get
- * Swapped. Let's re-swap them.
- */
-
- portword = vpx_fp_read(client, 0x126) & 0xeff;
-
- vpx_fp_write(client, 0x126, portword | 0x100);
-
-
- }
-
-
-
- void vpx_start_capture(struct i2c_client *client)
- {
- int portword;
-
-
- /* Enable VPortA, VPortB, Pixclk, HREF, VREF, FIELD, VACT, LLC, LLC2 */
-
- portword = vpx_read_byte(client, 0xf2) & 0xf0;
- portword |= 0x0f;
-
- vpx_write_byte(client, 0xf2, portword);
- }
-
- void vpx_stop_capture(struct i2c_client *client)
- {
- int portword;
-
- /* Disable VPortA, VPortB, Pixclk,
- HREF, VREF, FIELD, VACT, LLC, LLC2 */
-
- portword = vpx_read_byte(client, 0xf2) & 0xf0;
-
- vpx_write_byte(client, 0xf2, portword);
- }
-
- void vpx_set_window(struct i2c_client *client, struct video_window *vwin)
- {
- /* Winloadtab1 is loaded here. It is active for both fields.
- */
-
- int portword;
-
- switch(vmode)
- {
- case VIDEO_MODE_PAL: {
-
- /* Vertical Begin Scanline */
- vpx_fp_write(client, 0x120, 20);
-
- /* Horizontal Begin, Pixel */
- vpx_fp_write(client, 0x123, 5);
-
- /* Maximum Lines input 310: PAL is a 625 line s/m. */
- vpx_fp_write(client, 0x121, 305);
-
- /* Scaled number of Vertical Lines. */
- vwin->height =
- (vwin->height > 305 ?
- 300 : vwin->height) & 0x1ff;
-
- vwin->height =
- vwin->height < 24 ?
- 24 : vwin->height;
-
- /* Scaled Width of raster line. */
- vwin->width =
- (vwin->width > 800 ?
- 800 : vwin->width) & 0x7ff;
-
- vwin->width = vwin->width < 32 ?
- 32 : vwin->width;
-
- break;
- }
-
- default:
- return;
- }
-
- /* Update No. of Vlines to vpx */
-
- vpx_fp_write(client, 0x122, vwin->height);
-
- /* Width of raster, upto 800 pixels */
-
- vpx_fp_write(client, 0x125, vwin->width);
- vpx_fp_write(client, 0x124, vwin->width);
-
-
- /* Latch current Values */
- portword = vpx_fp_read(client, 0x140);
- portword |= 0x20;
- vpx_fp_write(client, 0x140, portword);
-
-
- }
-
-
- void vpx_set_picture(struct i2c_client *client, struct video_picture *vpict)
- {
-
- int portword;
-
- tprintk("Brightness: %d, Contrast: %d, Colour: %d, Hue %d \n",
- vpict->brightness, vpict->contrast, vpict->colour,
- vpict->hue);
-
- /* set contrast FPReg 0x132 [5].[4:0] = contrast ratio */
-
- portword = vpx_fp_read(client, 0x132) & 0xfd0;
-
- portword |= (vpict->contrast >> 10) & 0x3f;
-
- vpx_fp_write(client, 0x132, portword);
-
- tprintk("H/W contrast set to %d.%d \n", vpict->contrast >> 15,
- vpict->contrast >> 11 & 0x1f);
-
- /* Set Brightness */
- portword = vpx_fp_read(client, 0x131) & 0xf00;
-
- portword |= vpict->brightness >> 8;
-
- vpx_fp_write(client, 0x131, portword);
-
-
-
-
- }
-
- int vpx_detect_client(struct i2c_adapter *adapter, int address,
- unsigned short flags, int kind)
- {
- int err = 0;
-
- struct i2c_client *new_client;
-
- dprintk("in vpx_detect_client() \n");
-
- if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_EMUL)){
- return 0;
- }
-
-
- if(! (new_client =
- kmalloc(sizeof(struct i2c_client),GFP_KERNEL))) {
-
- return -ENOMEM;
- }
-
- new_client->addr = address;
- new_client->adapter = adapter;
- new_client->driver = &vpx_driver;
- new_client->flags = 0;
-
- /* Now, we do the remaining detection. No `force' parameter is used. */
-
- if (vpx_read_byte(new_client, VPX_REG_JEDEC) != VPX_JEDEC_ID)
- {
- goto BAILOUT;
- }
- if (vpx_read_byte(new_client, VPX_REG_PARTNUM1) != VPX_PARTNUM1_ID)
- {
- goto BAILOUT;
- }
-
- if ((vpx_read_byte(new_client, VPX_REG_PARTNUM0) == VPX_3224D_ID)){
-
- printk( KERN_INFO
- "vpx: Found: vpx 3224d chip @ 0x%02x \n",
- (new_client->addr << 1) );
-
- if((err=vpx_init_client(new_client))) goto BAILOUT;
- return 0;
- }
-
- if (vpx_read_byte(new_client,
- VPX_REG_PARTNUM0) == VPX_3225D_ID){
- printk( KERN_INFO
- "vpx: Found: vpx 3225d chip @ 0x%02x \n",
- (new_client->addr << 1));
-
- if((err=vpx_init_client(new_client))) goto BAILOUT;
- return 0;
- }
- else{
- printk( KERN_WARNING
- "vpx: Can't find supported vpx chip
- on this adapter. Sorry.\n");
-
-
- goto BAILOUT;
- }
-
-
- BAILOUT:
- kfree(new_client);
- return err;
-
- }
-
-
-
- int vpx_init_client(struct i2c_client *client)
- {
-
- int err=0;
-
-
- /* This driver is non re-entrant; ie; can only support one chip
- at a time.
- */
-
- if(re_entry)
- {
- printk(KERN_INFO
- "Sorry, only one client allowed per adapter. \n");
- return -EBUSY;
- }
-
- re_entry = 1;
-
- /* Fill in the remaining client fields. */
- strcpy(client->name, client_name);
-
-
- /* Tell the i2c layer a new client has arrived */
-
- if ((err = i2c_attach_client(client)))
- {
- return err;
- }
-
- /* Finally link this one client with our global client pointer */
- vpx_client = client;
-
- return 0;
- }
-
-
-
- int vpx_detach_client(struct i2c_client *client)
- {
- int err;
-
-
- /* Try to detach the client from i2c space */
- if ((err = i2c_detach_client(client))) {
- printk("vpx322xd: Client deregistration failed, client not detached.\n");
-
- return err;
- }
-
- re_entry=0;
-
- MOD_DEC_USE_COUNT;
-
- /* Frees client data too, if allocated at the same time */
- kfree(client);
- return 0;
- }
-
- int vpx_command(struct i2c_client *client, unsigned int cmd, void *arg)
- {
- /* Please note that it is the caller's responsibility to
- * maintain state information about the video processor.
- * The only VPROC_GET command is
- * used to query the video standard. ie; PAL, NTSC and such.
- */
-
- switch(cmd)
- {
- case VPROC_INIT:
- {
- int model = * ((int *) arg);
-
- vpx_pinit(client, model);
- dprintk("VPROC_INIT called. \n");
- break;
- }
-
- case VPROC_START_CAPTURE:
-
- vpx_start_capture(client);
-
- dprintk("VPROC_START_CAPTURE called. \n");
- break;
-
-
- case VPROC_STOP_CAPTURE:
-
- vpx_stop_capture(client);
-
- dprintk("VPROC_STOP_CAPTURE called. \n");
- break;
-
-
- case VPROC_SET_CAP_MODE:
-
- /* SET to PAL/NTSC/SECAM via struct */
- vmode = *((int *)(arg));
- dprintk("VPROC_SET_CAP_MODE called. \n");
- break;
-
-
- case VPROC_GET_CAP_MODE:
- memcpy(arg, &vmode, sizeof(int));
- break;
-
- case VPROC_SET_WINDOW:
- {
- struct video_window * vwin = arg;
- vpx_set_window(client, vwin);
- dprintk("VPROC_SET_WINDOW called. \n");
- break;
- }
-
- case VPROC_SET_PICTURE:
- {
- struct video_picture * vpict = arg;
- vpx_set_picture(client, vpict);
- dprintk("VPROC_SET_PICTURE called. \n");
- break;
- }
- }
-
-
- return 0;
- }
-
- int vpx_attach_adapter(struct i2c_adapter *adapter)
- {
- int retval;
- switch ((adapter->id & I2C_ALGO_BIT)) {
- case I2C_ALGO_BIT:
-
- printk(KERN_INFO "vpx: probing i2c adapter %s ... \n",
- adapter->name);
- retval = i2c_probe(adapter,&addr_data,&vpx_detect_client);
-
- break;
-
- default:
-
- printk("vpx: skipping adapter %s,
- adapter type not supported.\n",
- adapter->name);
- retval = 0;
- }
-
- /* Had to put this here because of messy code in vpx_detect_client()
- * TODO: Cleanup the chip detect code in vpx_detect_client().
- */
-
- if(!retval)
- MOD_INC_USE_COUNT;
-
- return retval;
-
- }
-
- void vpx_inc_use(struct i2c_client *client)
- {
- MOD_INC_USE_COUNT;
- }
-
- void vpx_dec_use(struct i2c_client *client)
- {
- MOD_DEC_USE_COUNT;
- }
-
-
-
-
- int __init vpx_init(void)
- {
- int res;
- printk("vpx version %s (%s)\n", VPX_VERSION, VPX_DATE);
-
- if ((res = i2c_add_driver(&vpx_driver))) {
- printk("vpx: Driver registration failed, module not inserted.\n");
- return res;
- }
-
- return 0;
- }
-
- int __init vpx_cleanup(void)
- {
- int res;
- if ((res = i2c_del_driver(&vpx_driver))) {
- printk("vpx: Driver de-registration failed, module not removed.\n");
- return res;
- }
-
-
- return 0;
- }
-
-
- EXPORT_NO_SYMBOLS;
- MODULE_PARM(debug, "i");
-
-
- MODULE_PARM_DESC(debug,
- "debug level - 0 off; 1 normal; 2 verbose; ");
-
- #ifdef MODULE
- MODULE_AUTHOR("Cherry George Mathew <cherry@freeshell.org>");
- MODULE_DESCRIPTION("i2c driver for the micronas vpx322xd
- video processor family");
- MODULE_LICENSE("GPL");
-
-
- int init_module(void)
- {
- return vpx_init();
- }
-
-
- int cleanup_module(void)
- {
- return vpx_cleanup();
- }
-
- #endif /* MODULE */
-
-
-
-
- /*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
-
-
-
-
-